// 方法语法
/// 方法 与函数类似：它们使用 fn 关键字和名称声明，可以拥有参数和返回值，
/// 同时包含在某处调用该方法时会执行的代码。不过方法与函数是不同的，
/// 因为它们在结构体的上下文中被定义（或者是枚举或 trait 对象的上下文，
/// 并且它们第一个参数总是 self，它代表调用该方法的结构体实例。


#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}


/// 定义方法，类似 Java 实体类中的方法
// 为了使函数定义于 Rectangle 的上下文中，我们开始了一个 impl 块（impl 是 implementation 的缩写），
// 这个 impl 块中的所有内容都将与 Rectangle 类型相关联。即相当于 java 的同处一个文件内
impl Rectangle {
    // 方法的第一个参数总是 self，它代表调用该方法的结构体实例
    fn area(&self) -> u32 {
        self.width * self.height
    }
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

fn methods_1() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        rect1.area()
    );
}


/// 关联函数，关联函数经常被用作返回一个结构体新实例的构造函数
// 所有在 impl 块中定义的函数被称为关联函数（associated function），
// 因为它们与 impl 后面命名的类型相关。我们可以定义不以 self 为第一参数的关联函数（因此不是方法），
// 因为它们并不作用于一个结构体的实例。
impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}

fn methods_2() {
    let sq = Rectangle::square(3);
}


// 每个结构体都允许拥有多个 impl 块
// 这里没有理由将这些方法分散在多个 impl 块中，不过这是有效的语法。所以在实际的开发中需要综合考虑
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

fn methods_4() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    let rect2 = Rectangle {
        width: 10,
        height: 40,
    };
    let rect3 = Rectangle {
        width: 60,
        height: 45,
    };

    println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
    println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}
